package com.syf.kit.es.core;

import com.syf.core.kit.SyFrameKit;
import org.elasticsearch.index.query.BoolQueryBuilder;
import org.elasticsearch.index.query.QueryBuilders;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.data.elasticsearch.core.ElasticsearchOperations;
import org.springframework.data.elasticsearch.core.SearchHits;
import org.springframework.data.elasticsearch.core.query.Criteria;
import org.springframework.data.elasticsearch.core.query.CriteriaQuery;
import org.springframework.data.elasticsearch.core.query.NativeSearchQuery;
import org.springframework.data.elasticsearch.core.query.NativeSearchQueryBuilder;
import org.springframework.data.elasticsearch.core.query.Query;
import org.springframework.data.elasticsearch.core.mapping.IndexCoordinates;
import org.springframework.data.domain.Page;
import org.springframework.data.domain.Pageable;
import org.springframework.data.elasticsearch.core.SearchHitSupport;
import org.springframework.data.elasticsearch.core.SearchPage;
import org.springframework.data.elasticsearch.core.SearchHit;
import org.springframework.data.domain.PageImpl;
import org.springframework.data.domain.PageRequest;
import org.springframework.data.domain.Range;
import org.elasticsearch.index.query.RangeQueryBuilder;

import java.util.ArrayList;
import java.util.Arrays;
import java.util.Collections;
import java.util.List;
import java.util.Map;

/**
 * Elasticsearch对象操作客户端
 */
@SyFrameKit("es client 操作bean对象")
public class SyEsObjectClient {
    private static final Logger logger = LoggerFactory.getLogger(SyEsObjectClient.class);

    private final ElasticsearchOperations elasticsearchOperations;

    public SyEsObjectClient(ElasticsearchOperations elasticsearchOperations) {
        this.elasticsearchOperations = elasticsearchOperations;
    }

    /**
     * 检查索引是否存在
     *
     * @param clazz 文档类型
     * @return 是否存在
     */
    public <T> boolean indexExists(Class<T> clazz) {
        try {
            return elasticsearchOperations.indexOps(clazz).exists();
        } catch (Exception e) {
            logger.error("检查索引是否存在失败: class={}, error={}", clazz, e.getMessage(), e);
            return false;
        }
    }

    /**
     * 创建索引
     *
     * @param clazz 文档类型
     * @return 是否成功
     */
    public <T> boolean createIndex(Class<T> clazz) {
        try {
            if (!indexExists(clazz)) {
                elasticsearchOperations.indexOps(clazz).create();
                logger.info("创建索引成功: {}", elasticsearchOperations.indexOps(clazz).getIndexCoordinates().getIndexName());
                return true;
            }
            return true;
        } catch (Exception e) {
            logger.error("创建索引失败: class={}, error={}", clazz, e.getMessage(), e);
            return false;
        }
    }

    /**
     * 删除索引
     *
     * @param clazz 文档类型
     * @return 是否成功
     */
    public <T> boolean deleteIndex(Class<T> clazz) {
        try {
            if (indexExists(clazz)) {
                elasticsearchOperations.indexOps(clazz).delete();
                logger.info("删除索引成功: {}", elasticsearchOperations.indexOps(clazz).getIndexCoordinates().getIndexName());
                return true;
            }
            return true;
        } catch (Exception e) {
            logger.error("删除索引失败: class={}, error={}", clazz, e.getMessage(), e);
            return false;
        }
    }

    /**
     * 获取索引名称
     *
     * @param clazz 文档类型
     * @return 索引名称
     */
    public <T> String getIndexName(Class<T> clazz) {
        try {
            return elasticsearchOperations.indexOps(clazz).getIndexCoordinates().getIndexName();
        } catch (Exception e) {
            logger.error("获取索引名称失败: class={}, error={}", clazz, e.getMessage(), e);
            return null;
        }
    }

    /**
     * 获取索引映射
     *
     * @param clazz 文档类型
     * @return 索引映射
     */
    public <T> String getIndexMapping(Class<T> clazz) {
        try {
            return elasticsearchOperations.indexOps(clazz).getMapping().toString();
        } catch (Exception e) {
            logger.error("获取索引映射失败: class={}, error={}", clazz, e.getMessage(), e);
            return null;
        }
    }

    /**
     * 创建文档
     *
     * @param document 文档对象
     * @return 是否成功
     */
    public <T> boolean createDocument(T document) {
        try {
            T saved = elasticsearchOperations.save(document);
            return saved != null;
        } catch (Exception e) {
            logger.error("创建文档失败: document={}, error={}", document, e.getMessage(), e);
            return false;
        }
    }

    /**
     * 更新文档
     *
     * @param document 文档对象
     * @return 是否成功
     */
    public <T> boolean updateDocument(T document) {
        try {
            T updated = elasticsearchOperations.save(document);
            return updated != null;
        } catch (Exception e) {
            logger.error("更新文档失败: document={}, error={}", document, e.getMessage(), e);
            return false;
        }
    }

    /**
     * 删除文档
     *
     * @param id    文档ID
     * @param clazz 文档类型
     * @return 是否成功
     */
    public <T> boolean deleteDocument(String id, Class<T> clazz) {
        try {
            String documentId = elasticsearchOperations.delete(id, clazz);
            return documentId != null;
        } catch (Exception e) {
            logger.error("删除文档失败: id={}, class={}, error={}", id, clazz, e.getMessage(), e);
            return false;
        }
    }

    /**
     * 获取文档
     *
     * @param id    文档ID
     * @param clazz 文档类型
     * @return 文档对象
     */
    public <T> T getDocument(String id, Class<T> clazz) {
        try {
            return elasticsearchOperations.get(id, clazz);
        } catch (Exception e) {
            logger.error("获取文档失败: id={}, class={}, error={}", id, clazz, e.getMessage(), e);
            return null;
        }
    }

    /**
     * 批量创建文档
     *
     * @param documents 文档对象列表
     * @return 是否全部成功
     */
    public <T> boolean bulkCreate(List<T> documents) {
        try {
            Iterable<T> saved = elasticsearchOperations.save(documents);
            return saved != null;
        } catch (Exception e) {
            logger.error("批量创建文档失败: documents={}, error={}", documents, e.getMessage(), e);
            return false;
        }
    }

    /**
     * 搜索文档（文本模糊匹配）
     *
     * @param query 查询条件，key为字段名，value为查询值
     * @param clazz 文档类型
     * @return 搜索结果
     */
    public <T> List<T> search(Map<String, Object> query, Class<T> clazz) {
        try {
            NativeSearchQueryBuilder queryBuilder = new NativeSearchQueryBuilder();

            if (query.size() > 1) {
                // 多个查询条件使用OR连接
                BoolQueryBuilder boolQuery = QueryBuilders.boolQuery();
                query.forEach((field, value) -> {
                    if (value instanceof Number) {
                        boolQuery.should(QueryBuilders.termQuery(field, value));
                    } else {
                        boolQuery.should(QueryBuilders.matchQuery(field, value.toString()));
                    }
                });
                queryBuilder.withQuery(boolQuery);
            } else {
                // 单个查询条件
                Map.Entry<String, Object> entry = query.entrySet().iterator().next();
                if (entry.getValue() instanceof Number) {
                    queryBuilder.withQuery(QueryBuilders.termQuery(entry.getKey(), entry.getValue()));
                } else {
                    queryBuilder.withQuery(QueryBuilders.matchQuery(entry.getKey(), entry.getValue().toString()));
                }
            }

            NativeSearchQuery searchQuery = queryBuilder.build();
            System.out.println("生成的查询语句：" + searchQuery.getQuery().toString());

            SearchHits<T> searchHits = elasticsearchOperations.search(searchQuery, clazz);
            System.out.println("查询结果总数：" + searchHits.getTotalHits());

            List<T> results = new ArrayList<>();
            searchHits.forEach(hit -> results.add(hit.getContent()));
            return results;
        } catch (Exception e) {
            logger.error("搜索文档失败: query={}, class={}, error={}", query, clazz, e.getMessage(), e);
            return new ArrayList<>();
        }
    }

    /**
     * 精准搜索文档（keyword类型字段的精确匹配）
     *
     * @param query 查询条件，key为字段名，value为查询值
     * @param clazz 文档类型
     * @return 搜索结果
     */
    public <T> List<T> searchExact(Map<String, Object> query, Class<T> clazz) {
        try {
            NativeSearchQueryBuilder queryBuilder = new NativeSearchQueryBuilder();

            if (query.size() > 1) {
                // 多个查询条件使用AND连接
                BoolQueryBuilder boolQuery = QueryBuilders.boolQuery();
                query.forEach((field, value) -> {
                    if (value instanceof Number) {
                        boolQuery.must(QueryBuilders.termQuery(field, value));
                    } else {
                        boolQuery.must(QueryBuilders.termQuery(field + ".keyword", value.toString()));
                    }
                });
                queryBuilder.withQuery(boolQuery);
            } else {
                // 单个查询条件
                Map.Entry<String, Object> entry = query.entrySet().iterator().next();
                if (entry.getValue() instanceof Number) {
                    queryBuilder.withQuery(QueryBuilders.termQuery(entry.getKey(), entry.getValue()));
                } else {
                    queryBuilder.withQuery(
                            QueryBuilders.termQuery(entry.getKey() + ".keyword", entry.getValue().toString()));
                }
            }

            NativeSearchQuery searchQuery = queryBuilder.build();
            System.out.println("生成的查询语句：" + searchQuery.getQuery().toString());

            SearchHits<T> searchHits = elasticsearchOperations.search(searchQuery, clazz);
            System.out.println("查询结果总数：" + searchHits.getTotalHits());

            List<T> results = new ArrayList<>();
            searchHits.forEach(hit -> results.add(hit.getContent()));
            return results;
        } catch (Exception e) {
            logger.error("精准搜索文档失败: query={}, class={}, error={}", query, clazz, e.getMessage(), e);
            return new ArrayList<>();
        }
    }

    /**
     * 获取原生客户端
     *
     * @return ElasticsearchOperations实例
     */
    public ElasticsearchOperations getClient() {
        return elasticsearchOperations;
    }

    /**
     * 按条件进行分页查询
     *
     * @param query      查询条件，key为字段名，value为查询值
     * @param exactMatch 是否精确匹配 (true: AND连接, false: OR连接)
     * @param pageable   分页参数
     * @param clazz      文档类型
     * @return 分页结果
     */
    public <T> Page<SearchHit<T>> searchWithPagination(Map<String, Object> query, boolean exactMatch, Pageable pageable,
            Class<T> clazz) {
        try {
            NativeSearchQueryBuilder queryBuilder = new NativeSearchQueryBuilder();
            queryBuilder.withPageable(pageable);

            if (query != null && !query.isEmpty()) {
                BoolQueryBuilder boolQuery = QueryBuilders.boolQuery();
                query.forEach((field, value) -> {
                    if (exactMatch) {
                        // 精确匹配，使用AND连接
                        if (value instanceof Number) {
                            boolQuery.must(QueryBuilders.termQuery(field, value));
                        } else {
                            boolQuery.must(QueryBuilders.termQuery(field + ".keyword", value.toString()));
                        }
                    } else {
                        // 模糊匹配，使用OR连接
                        if (value instanceof Number) {
                            boolQuery.should(QueryBuilders.termQuery(field, value));
                        } else {
                            boolQuery.should(QueryBuilders.matchQuery(field, value.toString()));
                        }
                    }
                });

                if (!exactMatch && !query.isEmpty()) {
                    // 对于OR查询，至少要匹配一个条件
                    boolQuery.minimumShouldMatch(1);
                }

                queryBuilder.withQuery(boolQuery);
            }

            NativeSearchQuery searchQuery = queryBuilder.build();
            logger.debug("生成的ES查询语句: {}", searchQuery.getQuery());

            SearchHits<T> searchHits = elasticsearchOperations.search(searchQuery, clazz);
            SearchPage<T> page = SearchHitSupport.searchPageFor(searchHits, pageable);
            return page;

        } catch (Exception e) {
            logger.error("分页搜索文档失败: query={}, class={}, error={}", query, clazz, e.getMessage(), e);
            return Page.empty(pageable);
        }
    }

    /**
     * 使用search_after进行高效的分页滚动查询，支持区间过滤。
     *
     * @param query             查询条件，key为字段名，value为查询值
     * @param exactMatch        是否精确匹配 (true: AND连接, false: OR连接)
     * @param rangeQuery        区间查询条件，key为字段名，value为区间对象（如timestamp）
     * @param pageable          分页和排序参数。排序是必需的，且排序键应能唯一标识文档（推荐最后加上_id）！
     * @param searchAfterValues 上一页最后一条记录的排序值，用于获取下一页。第一页查询时应为null。
     * @param clazz             文档类型
     * @return 包含结果和下一页searchAfter值的分页对象
     */
    public <T> Page<SearchHit<T>> searchAfter(
            Map<String, Object> query,
            boolean exactMatch,
            Map<String, Range<?>> rangeQuery,
            Pageable pageable,
            Object[] searchAfterValues,
            Class<T> clazz) {
        if (pageable.getSort().isUnsorted()) {
            throw new IllegalArgumentException("使用search_after分页时必须提供排序(Sort)条件");
        }

        try {
            NativeSearchQueryBuilder queryBuilder = new NativeSearchQueryBuilder();
            queryBuilder.withPageable(PageRequest.of(0, pageable.getPageSize(), pageable.getSort()));

            if (searchAfterValues != null && searchAfterValues.length > 0) {
                queryBuilder.withSearchAfter(Arrays.asList(searchAfterValues));
            }

            BoolQueryBuilder boolQuery = QueryBuilders.boolQuery();
            // 普通精确/模糊查询
            if (query != null && !query.isEmpty()) {
                query.forEach((field, value) -> {
                    if (exactMatch) {
                        if (value instanceof Number) {
                            boolQuery.must(QueryBuilders.termQuery(field, value));
                        } else {
                            if ("_id".equals(field)) {
                                boolQuery.must(QueryBuilders.termQuery("_id", value.toString()));
                            } else {
                                boolQuery.must(QueryBuilders.termQuery(field + ".keyword", value.toString()));
                            }
                        }
                    } else {
                        if (value instanceof Number) {
                            boolQuery.should(QueryBuilders.termQuery(field, value));
                        } else {
                            if ("_id".equals(field)) {
                                boolQuery.should(QueryBuilders.termQuery("_id", value.toString()));
                            } else {
                                boolQuery.should(QueryBuilders.matchQuery(field, value.toString()));
                            }
                        }
                    }
                });
                if (!exactMatch && !query.isEmpty()) {
                    boolQuery.minimumShouldMatch(1);
                }
            }
            // 区间查询
            if (rangeQuery != null && !rangeQuery.isEmpty()) {
                rangeQuery.forEach((field, range) -> {
                    RangeQueryBuilder rangeBuilder = QueryBuilders.rangeQuery(field);
                    if (range.getLowerBound().isBounded()) {
                        rangeBuilder.gte(range.getLowerBound().getValue().orElse(null));
                    }
                    if (range.getUpperBound().isBounded()) {
                        rangeBuilder.lte(range.getUpperBound().getValue().orElse(null));
                    }
                    boolQuery.must(rangeBuilder);
                });
            }
            if (boolQuery.hasClauses()) {
                queryBuilder.withQuery(boolQuery);
            }

            NativeSearchQuery searchQuery = queryBuilder.build();
            searchQuery.setTrackTotalHits(false);
            logger.debug("生成的search_after查询语句: {}", searchQuery.getQuery());

            SearchHits<T> searchHits = elasticsearchOperations.search(searchQuery, clazz);
            return new PageImpl<>(searchHits.getSearchHits(), pageable, searchHits.getTotalHits());
        } catch (Exception e) {
            logger.error("search_after分页搜索文档失败: query={}, class={}, error={}", query, clazz, e.getMessage(), e);
            return Page.empty(pageable);
        }
    }
}